import datetime
import sys
import os
import numpy
import qdarkstyle
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QDoubleValidator, QRegExpValidator
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QProgressBar, QMessageBox
# from PyQt5.QtCore import pyqtSignal, QObject
from ui.ui_main import Ui_MainWindow

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas


from src.datahelper import StockDataHelper
from src.backTestingData import BackTestingData
from src.taskSignals import TaskSignals
from threading import Thread

from src.strategy.easystrategy import Easystrategy
from src.strategy.doubleMaStrategy import DoubleMaStrategy
from src.strategy.mlStrategy import MlStrategy
from src.log import Log
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker


class MainWindow(QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        # 回测基本数据对象
        self.backTestingData = BackTestingData()
        # 文件读写对象
        self.dataReader = StockDataHelper()
        # self.dataReader.init_goverment_loan()
        # 导入信号类
        self.taskSignals = TaskSignals()
        # 初始化主窗口内的组件
        self.setupUi()
        self.loger = Log()

        # 导入策略类
        self.strategies = {
            'easyStrategy': Easystrategy(),
            'doubleMaStrategy': DoubleMaStrategy(),
            'mlStrategy': MlStrategy()
        }
        self.currentStratey = None

        # 测试用
        path = os.path.join(os.getcwd(), 'data', 'partial_data.pkl')
        self.dataReader.init_data(path)
        self.taskSignals.loadFile.emit('已读入完成')

        self.ui.stock_list.selectAll()
        self.ui.addStockBtn.click()
        for i in range(self.ui.stock_list.count()):
            self.backTestingData.add_stock(self.ui.stock_list.item(i).text())

    def saveLog(self):
        now = datetime.datetime.now().strftime('%Y-%m-%d_%H_%M_%S')
        data = self.ui.logBrowser.toPlainText()

        def saveLog(fileName=now + ".txt"):

            self.loger.save(data, name=fileName)
            # self.dataReader.init_reform_data()
            self.taskSignals.loadFile.emit('写出日志完成')

        thread = Thread(target=saveLog)
        thread.start()

    def setupUi(self):
        '''--------画布--------'''

        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        self.ui.plotBox.addWidget(self.canvas)

        '''--------------校验相关start--------------'''
        # 输入框校验
        reg = QRegExp('[0-9]+$')
        validator = QRegExpValidator(self)
        validator.setRegExp(reg)

        self.ui.init_money.setValidator(validator)
        self.ui.trans_period.setValidator(validator)
        self.ui.back_day.setValidator(validator)
        self.ui.stock_nums.setValidator(validator)
        self.ui.long_ma_window.setValidator(validator)
        self.ui.short_ma_window.setValidator(validator)

        doubleValidator = QRegExpValidator(self)
        doubleValidator.setRegExp(QRegExp('^(0|(0.003)|(0.00[0-2]\d*))$'))
        self.ui.service_charge.setValidator(doubleValidator)
        '''--------------校验相关end--------------'''

        '''--------------信号相关start--------------'''
        # 添加信号与槽的链接
        # 信号
        self.taskSignals.loadFile.connect(self.loadFileSlot)
        self.taskSignals.logSignal.connect(self.log)
        self.taskSignals.progressBarSignal.connect(self.updateProgressBar)
        self.taskSignals.updateBackTestingSignal.connect(
            self.updateBackTestingResult)
        '''--------------信号相关end--------------'''

        '''--------------UI相关start--------------'''
        self.taskProcessBar = QProgressBar()
        # self.ui.statusbar.showMessage('asdf')
        self.taskProcessBar.setRange(0, 100)
        self.taskProcessBar.hide()
        self.ui.statusbar.addPermanentWidget(self.taskProcessBar)

        # 下拉框
        self.ui.factorSelect.addItems(['avg', 'pre_close', 'high_limit', 'low_limit', 'paused', 'change_pct', 'change', 'amplitude', 'up_to_limit', 'down_to_limit',
                                      'is_st', 'boll_down', 'CR20', 'boll_up', 'growth', 'TRIX5', 'beta', 'VOL5', 'WVAD', 'RSI', 'BBI', 'BIAS', 'MA', 'MTM', 'CCI', 'MACD', 'OBV', 'PSY'])
        # action事件
        self.ui.actionOpenfile.triggered.connect(self.openDataFile)
        self.ui.actionSaveLog.triggered.connect(self.saveLog)
        # 按钮
        self.ui.addStockBtn.clicked.connect(self.addStocks)
        self.ui.removeStockBtn.clicked.connect(self.removeStocks)
        self.ui.applyStrategyBtn.clicked.connect(self.applyStrategy)

        # 初始数据
        self.backTestingData.update_data(
            'money', int(self.ui.init_money.text()))
        self.backTestingData.update_data(
            'start_date', self.ui.start_date.text())
        self.backTestingData.update_data('end_date', self.ui.end_date.text())
        '''--------------UI相关end--------------'''

    def updateProgressBar(self, value: int):
        self.taskProcessBar.setValue(value)

    def runStrategy(self, strategyName):

        ui = self.ui
        start_date = ui.start_date.dateTime().toString("yyyy-MM-dd")
        end_date = ui.end_date.dateTime().toString("yyyy-MM-dd")
        service_charge = float(ui.service_charge.text())
        money = int(ui.init_money.text())

        self.taskProcessBar.show()
        self.currentStratey = self.strategies[strategyName]
        # 更新回测信息
        self.backTestingData.update_data('money', money)
        self.backTestingData.update_data('start_date', start_date)
        self.backTestingData.update_data('end_date', end_date)
        self.backTestingData.update_data('service_charge', service_charge)
        self.backTestingData.update_data(
            'selected_stocks', [ui.selected_stock_list.item(i).text() for i in range(ui.selected_stock_list.count())])
        self.backTestingData.update_data('portfolio_value', 0.0)
        self.ui.logBrowser.clear()
        # self.log(self.backTestingData.data['selected_stocks'].__str__())

        # 更新策略信息
        if strategyName == 'easyStrategy':
            trans_period = int(ui.trans_period.text())
            back_day = int(ui.back_day.text())
            stock_nums = int(ui.stock_nums.text())
            factor = ui.factorSelect.currentText()

            self.backTestingData.update_data('trans_period', trans_period)
            self.backTestingData.update_data('back_day', back_day)
            self.backTestingData.update_data('factor', factor)
            self.backTestingData.update_data('stock_nums', stock_nums)

        elif strategyName == 'doubleMaStrategy':
            long_ma = int(self.ui.long_ma_window.text())
            short_ma = int(self.ui.short_ma_window.text())

            self.backTestingData.update_data('long_ma', long_ma)
            self.backTestingData.update_data('short_ma', short_ma)

        elif strategyName == 'mlStrategy':

            window_size = int(self.ui.windowSizeLine.text())
            next_n_day = int(self.ui.ndayLine.text())
            transPeriod = int(self.ui.transPeriod.text())
            self.backTestingData.update_data('window_size', window_size)
            self.backTestingData.update_data('next_n_day', next_n_day)
            self.backTestingData.update_data('transPeriod', transPeriod)

        # self.log(self.backTestingData.data.__str__())

        self.currentStratey.init_strategy(
            self.dataReader, self.backTestingData, self.taskSignals)

        strategy_thread = Thread(target=self.currentStratey.run)
        strategy_thread.start()

    def applyStrategy(self):
        idx = self.ui.strategyTab.currentIndex()
        strategyName = self.ui.strategyTab.tabText(idx)
        self.ui.currentStrategyText.setText(strategyName)
        # easyStrategy, doubleMaStrategy
        self.runStrategy(
            self.ui.strategyTab.currentWidget().objectName())

    def log(self, text):
        self.ui.logBrowser.append(text)

    def addStocks(self):
        for item in self.ui.stock_list.selectedItems():
            self.ui.selected_stock_list.addItem(item.clone())
            self.ui.stock_list.takeItem(self.ui.stock_list.row(item))
            self.backTestingData.add_stock(item.text())
        if self.ui.stock_nums.text() == '':
            tot = len(self.backTestingData.data['selected_stocks'])
            self.ui.stock_nums.setText(str(min(3, tot)))

    def removeStocks(self):
        for item in self.ui.selected_stock_list.selectedItems():
            self.ui.stock_list.addItem(item.clone())
            self.ui.selected_stock_list.takeItem(
                self.ui.selected_stock_list.row(item))
            self.backTestingData.remove_stock(item.text())

    def updateBackTestingResult(self, data: dict):

        self.ui.final_money.setText("%.2f" % (data.get('final_money')))
        self.ui.PL_ratio.setText("%.2f" % (data.get('win_lose_rate')))
        self.ui.profit_rate.setText("%.2f" % (data.get('profit_rate')) + "%")
        self.ui.win_rate.setText("%.2f " % (data.get('win_rate')) + "%")
        self.ui.mx_withdraw.setText("%.2f " % (data.get('max_withdraw')) + "%")

        def drawFigure():
            trade_day_list = data.get('trade_day_list', [])
            daily_return = data.get('daily_return', [])
            
            # 画图
            ax = plt.gca()
            ax.cla()
            # tick_spacing = 15 #横坐标间隔
            tick_spacing = len(trade_day_list)/10  # 横坐标间隔
            ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
            # ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%.1f s'))
            ax.yaxis.set_major_formatter(
                ticker.PercentFormatter(xmax=100, decimals=1))

            plt.title("backTesing Result")
            plt.xlabel("time")
            plt.ylabel("profit")
            plt.xticks(rotation=10)  # 横坐标旋转
            x = numpy.array(trade_day_list)
            y = numpy.array(daily_return)
            if len(x) > len(y):
                x = x[:len(y)]
            plt.plot(x, y)

            # plt.scatter(trade_day_list, daily_return)
            self.canvas.draw()

        thread = Thread(target=drawFigure)
        thread.start()

    # 文件加载信号槽

    def loadFileSlot(self, msg):
        self.ui.statusbar.showMessage(msg)
        if msg == '已读入完成':
            startDate = datetime.datetime.strptime(
                self.dataReader.trade_day_list[0], '%Y-%m-%d')
            endDate = datetime.datetime.strptime(
                self.dataReader.trade_day_list[-1], '%Y-%m-%d')

            self.ui.start_date.setDate(startDate)
            self.ui.end_date.setDate(endDate)

            # 清除所有item
            while self.ui.stock_list.count() > 0:
                self.ui.stock_list.takeItem(0)
            while self.ui.selected_stock_list.count() > 0:
                self.ui.selected_stock_list.takeItem(0)
            # self.ui.stock_list.
            self.ui.stock_list.addItems(self.dataReader.stock_list)

    def openDataFile(self):
        fileName, type = QFileDialog.getOpenFileName(
            self, "选取文件", "/Users/yangwendeng/Documents/python-five-tanking-man/data",
            "packle Files (*.pkl)")

        def readData(fileName=fileName):
            self.taskSignals.loadFile.emit('正在读入文件')
            self.dataReader.init_data(fileName)
            # self.dataReader.init_reform_data()
            self.taskSignals.loadFile.emit('已读入完成')

        thread = Thread(target=readData)
        thread.start()


# 加载主窗口
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

    window.show()
    sys.exit(app.exec())
